package com.zzyl.nursing.service.impl;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.until.DateTimeZoneConverter;
import com.zzyl.nursing.vo.DeviceDataVo;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import javax.annotation.PostConstruct;
import java.util.Arrays;

/**
 * 设备管理Service业务层处理
 *
 * @author yjb
 * @date 2025-10-17
 */
@Service
@Slf4j
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired//注入华为云 用华为SDK方法
    private IoTDAClient client;
    @Autowired//注入redis
    private StringRedisTemplate redisTemplate;

    /**
     * 查询设备管理列表
     *
     * @param device 设备管理
     * @return 设备管理
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    @Override
    public void syncProductList() {

        log.info("开始同步产品列表到Redis...");
        //请求参数
        ListProductsRequest listProductsRequest = new ListProductsRequest();
        listProductsRequest.setLimit(50);//最大限制查询条数
        //发起请求
        ListProductsResponse response = client.listProducts(listProductsRequest);
        //判断是否成功
        if (response.getHttpStatusCode() != 200) {
            //不为200 抛异常
            throw new BaseException("同步失败,请稍后再试");
        }
        //成功 保存到redis
        List<ProductSummary> products = response.getProducts();
        redisTemplate.boundValueOps(CacheConstants.IOT_ALL_PRODUCT_LIST).set(JSONUtil.toJsonStr(products));
    }

    @Override
    public List<ProductVo> allProduct() {
        //从redis获取数据
        String result = redisTemplate.boundValueOps(CacheConstants.IOT_ALL_PRODUCT_LIST).get();
        //判断返回结果
        if (result != null) {
            List<ProductVo> productVos = JSONUtil.toList(result, ProductVo.class);
            return productVos;
        }
        //为空返回空集合
        return List.of();
    }

    /*注册设备
     *
     * */
    @Override
    public void register(DeviceDto dto) {
        // 查询设备名称是否重复
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getDeviceName, dto.getDeviceName());
        Device one = this.getOne(queryWrapper);
        if (one != null) {
            throw new BaseException("设备名称重复");
        }

        // 查询设备标识是否重复
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getNodeId, dto.getNodeId());
        Device one1 = this.getOne(queryWrapper);
        if (one1 != null) {
            throw new BaseException("设备标识重复");
        }

        // 查询同一个位置是否绑定了相同的产品
        //设备类型   具体位置  productKey
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getLocationType, dto.getLocationType());
        queryWrapper.eq(Device::getBindingLocation, dto.getBindingLocation());
        queryWrapper.eq(Device::getProductKey, dto.getProductKey());
        Device one3 = this.getOne(queryWrapper);
        if (one3 != null) {
            throw new BaseException("同一个位置绑定了相同的产品");
        }

        //1.先从iot里面获取 id
        AddDeviceRequest request = new AddDeviceRequest();
        //获取对象
        AddDevice addDevice = new AddDevice();
        //设备标识
        addDevice.setNodeId(dto.getNodeId());
        addDevice.setDeviceName(dto.getDeviceName());
        //这个就是获取key
        addDevice.setProductId(dto.getProductKey());
        //获取秘钥
        AuthInfo authInfo = new AuthInfo();
        String secret = RandomUtil.randomString(32);
        authInfo.setSecret(secret);
        addDevice.setAuthInfo(authInfo);
        addDevice.setDescription(dto.getDeviceDescription());
        //1.1从body获取
        request.setBody(addDevice);
        AddDeviceResponse addDeviceResponse = client.addDevice(request);
        //2.做判断是否存在
        if (addDeviceResponse.getHttpStatusCode() != 201) {
            throw new BaseException("设备没有注册失败");
        }
        //3.在获取自己表
        //获取设备id就是 iot id
        String deviceId = addDeviceResponse.getDeviceId();
        Device device = new Device();
        device.setIotId(deviceId);
        device.setSecret(secret);
        device.setBindingLocation(dto.getBindingLocation() + "");
        device.setLocationType(dto.getLocationType());
        device.setPhysicalLocationType(dto.getPhysicalLocationType());
        device.setDeviceName(dto.getDeviceName());
        device.setProductKey(dto.getProductKey());
        device.setProductName(dto.getProductName());
        device.setDeviceDescription(dto.getDeviceDescription());
        device.setNodeId(dto.getNodeId());
        device.setRemark(dto.getRemark());
        this.save(device);

    }

    @Override
    public DeviceDetailVo deviceDetail(String iotId) {
        //iot查询
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.setDeviceId(iotId);
        ShowDeviceResponse response = client.showDevice(request);
        if (response.getHttpStatusCode() != 200) {
            throw new BaseException("设备查询失败");
        }
        //本地查询
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getIotId, iotId);
        Device device = this.getOne(queryWrapper);
        if (device == null) {
            throw new BaseException("设备不存在");
        }

        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        //设备状态
        deviceDetailVo.setDeviceStatus(response.getStatus());

        String activeTime = response.getActiveTime();
        //转成字符串
        LocalDateTime parse = LocalDateTimeUtil.parse(activeTime, DatePattern.UTC_MS_PATTERN);
        LocalDateTime actime = DateTimeZoneConverter.utcToShanghai(parse);
        deviceDetailVo.setActiveTime(actime);
        return deviceDetailVo;
    }

    @Override
    public List<DeviceDataVo> queryServiceProperties(String iotId) {
        List<DeviceDataVo> list = new ArrayList<>();
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        //获取的是哪个设备  传id
        request.setDeviceId(iotId);
        ShowDeviceShadowResponse response = client.showDeviceShadow(request);
        //调用shadow方法
        List<DeviceShadowData> deviceShadowData = response.getShadow();
        DeviceShadowData shadowData = deviceShadowData.get(0);
        DeviceShadowProperties reported = shadowData.getReported();
        //时间需要转
        String eventTime = reported.getEventTime();
        LocalDateTime parse = LocalDateTimeUtil.parse(eventTime, "yyyyMMdd'T'HHmmss'Z'");
        LocalDateTime actime = DateTimeZoneConverter.utcToShanghai(parse);

        //表面是object  实际是 LinkedHashMap  直接强转
        LinkedHashMap<String, Object> linkedHashMap = (LinkedHashMap) reported.getProperties();
        for (String key : linkedHashMap.keySet()) {
            DeviceDataVo deviceDataVo = new DeviceDataVo();
            deviceDataVo.setFunctionId(key);
            deviceDataVo.setEventTime(actime);
            deviceDataVo.setValue(linkedHashMap.get(key));
            list.add(deviceDataVo);
        }

        // 返回集合
        return list;
    }

    /*
     * 修改设备*/

    /**
     * 修改设备（根据nodeId查询，存在则修改，不存在则新增）
     */
    @Override
    public void updateDevice(DeviceDto dto) {
        // 根据nodeId查询设备是否存在
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getNodeId, dto.getNodeId());
        Device existingDevice = this.getOne(queryWrapper);
        if (existingDevice != null) {
            // 设备存在，执行修改逻辑
            // 校验必填参数
            if (dto.getId() == null) {
                throw new BaseException("设备ID不能为空");
            }

            // 查询设备名称是否重复（排除当前设备）
            LambdaQueryWrapper<Device> nameQueryWrapper = new LambdaQueryWrapper<>();
            nameQueryWrapper.eq(Device::getDeviceName, dto.getDeviceName())
                    .ne(Device::getId, dto.getId());
            Device nameDuplicate = this.getOne(nameQueryWrapper);
            if (nameDuplicate != null) {
                throw new BaseException("设备名称重复");
            }

            // 查询设备标识是否重复（排除当前设备）
            LambdaQueryWrapper<Device> nodeIdQueryWrapper = new LambdaQueryWrapper<>();
            nodeIdQueryWrapper.eq(Device::getNodeId, dto.getNodeId())
                    .ne(Device::getId, dto.getId());
            Device nodeIdDuplicate = this.getOne(nodeIdQueryWrapper);
            if (nodeIdDuplicate != null) {
                throw new BaseException("设备标识重复");
            }

            // 查询同一个位置是否绑定了相同的产品（排除当前设备）
            LambdaQueryWrapper<Device> locationQueryWrapper = new LambdaQueryWrapper<>();
            locationQueryWrapper.eq(Device::getLocationType, dto.getLocationType())
                    .eq(Device::getBindingLocation, dto.getBindingLocation())
                    .eq(Device::getProductKey, dto.getProductKey())
                    .ne(Device::getId, dto.getId());
            Device locationDuplicate = this.getOne(locationQueryWrapper);
            if (locationDuplicate != null) {
                throw new BaseException("同一个位置绑定了相同的产品");
            }


            // 1. 先修改IoT平台设备

            UpdateDeviceRequest request = new UpdateDeviceRequest();

            // 设置要修改的设备ID（IoT平台的设备ID）
            request.setDeviceId(existingDevice.getIotId());

            // 创建更新对象
            UpdateDevice updateDevice = new UpdateDevice();
            updateDevice.setDeviceName(dto.getDeviceName());
            updateDevice.setDescription(dto.getDeviceDescription());

            request.setBody(updateDevice);

            // 调用IoT平台接口更新设备
            UpdateDeviceResponse updateDeviceResponse = client.updateDevice(request);

            // 判断IoT平台更新是否成功
            if (updateDeviceResponse.getHttpStatusCode() != 200) {
                throw new BaseException("IoT平台设备更新失败");
            }

            // 2. 更新本地数据库
            Device device = new Device();
            device.setId(dto.getId());
            device.setBindingLocation(dto.getBindingLocation() + "");
            device.setLocationType(dto.getLocationType());
            device.setPhysicalLocationType(dto.getPhysicalLocationType());
            device.setDeviceName(dto.getDeviceName());
            device.setProductKey(dto.getProductKey());
            device.setProductName(dto.getProductName());
            device.setDeviceDescription(dto.getDeviceDescription());
            device.setNodeId(dto.getNodeId());
            device.setRemark(dto.getRemark());

            boolean updateResult = this.updateById(device);
            if (!updateResult) {
                throw new BaseException("本地设备更新失败");
            }
        } else {
            // 设备不存在，执行新增逻辑
            // 查询设备名称是否重复
            LambdaQueryWrapper<Device> nameQueryWrapper = new LambdaQueryWrapper<>();
            nameQueryWrapper.eq(Device::getDeviceName, dto.getDeviceName());
            Device one = this.getOne(nameQueryWrapper);
            if (one != null) {
                throw new BaseException("设备名称重复");
            }

            // 查询设备标识是否重复
            LambdaQueryWrapper<Device> nodeIdQueryWrapper = new LambdaQueryWrapper<>();
            nodeIdQueryWrapper.eq(Device::getNodeId, dto.getNodeId());
            Device one1 = this.getOne(nodeIdQueryWrapper);
            if (one1 != null) {
                throw new BaseException("设备标识重复");
            }

            // 查询同一个位置是否绑定了相同的产品
            LambdaQueryWrapper<Device> locationQueryWrapper = new LambdaQueryWrapper<>();
            locationQueryWrapper.eq(Device::getLocationType, dto.getLocationType())
                    .eq(Device::getBindingLocation, dto.getBindingLocation())
                    .eq(Device::getProductKey, dto.getProductKey());
            Device one3 = this.getOne(locationQueryWrapper);
            if (one3 != null) {
                throw new BaseException("同一个位置绑定了相同的产品");
            }

            // 1. 先从iot里面获取id
            AddDeviceRequest request = new AddDeviceRequest();
            // 获取对象
            AddDevice addDevice = new AddDevice();
            // 设备标识
            addDevice.setNodeId(dto.getNodeId());
            addDevice.setDeviceName(dto.getDeviceName());
            // 这个就是获取key
            addDevice.setProductId(dto.getProductKey());
            // 获取秘钥
            AuthInfo authInfo = new AuthInfo();
            String secret = RandomUtil.randomString(32);
            authInfo.setSecret(secret);
            addDevice.setAuthInfo(authInfo);
            addDevice.setDescription(dto.getDeviceDescription());
            // 1.1从body获取
            request.setBody(addDevice);
            AddDeviceResponse addDeviceResponse = client.addDevice(request);
            // 2.做判断是否存在
            if (addDeviceResponse.getHttpStatusCode() != 201) {
                throw new BaseException("设备注册失败");
            }
            // 3.在获取自己表
            // 获取设备id就是iot id
            String deviceId = addDeviceResponse.getDeviceId();
            Device device = new Device();
            device.setIotId(deviceId);
            device.setSecret(secret);
            device.setBindingLocation(dto.getBindingLocation() + "");
            device.setLocationType(dto.getLocationType());
            device.setPhysicalLocationType(dto.getPhysicalLocationType());
            device.setDeviceName(dto.getDeviceName());
            device.setProductKey(dto.getProductKey());
            device.setProductName(dto.getProductName());
            device.setDeviceDescription(dto.getDeviceDescription());
            device.setNodeId(dto.getNodeId());
            device.setRemark(dto.getRemark());
            this.save(device);
        }
    }

    /*
     * 根据id删除设备*/
    @Override
    public void deleteDevice(String iotId) {
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        //判断有没有此设备  有的话再删
        queryWrapper.eq(Device::getIotId, iotId);
        Device one = this.getOne(queryWrapper);
        if (one == null) {
            throw new BaseException("设备不存在");
        }
        //查到了删除iot
        DeleteDeviceRequest request = new DeleteDeviceRequest();
        //设置需要删除的id
        request.setDeviceId(iotId);
        DeleteDeviceResponse deleteDeviceResponse = client.deleteDevice(request);
        //判断iot有没有删除成功
        if (deleteDeviceResponse.getHttpStatusCode() != 204) {
            throw new BaseException("iot平台删除失败");
        }
        //删除成功再去删除本地
        boolean result = this.removeById(one.getId());
        if (!result) {
            throw new BaseException("本地删除设备失败");
        }
    }

    /*
     * 报警规则使用 查询产品详情*/
    @Override
    public AjaxResult queryProductById(String productKey) {
        //调用iot方法
        ShowProductRequest request = new ShowProductRequest();
        request.setProductId(productKey);
        ShowProductResponse response = client.showProduct(request);
        if (response.getHttpStatusCode() != 200) {
        throw  new BaseException("查询产品失败");
        }
        List<ServiceCapability> serviceCapabilities = response.getServiceCapabilities();
        return AjaxResult.success(serviceCapabilities);
    }


    /**
     * 测试Redis连接
     */
    @PostConstruct
    public void testRedisOnStartup() {
        try {
            log.info("🔍 开始测试Redis连接...");
            redisTemplate.opsForValue().set("test_connection", "success", Duration.ofSeconds(30));
            String result = redisTemplate.opsForValue().get("test_connection");
            log.info("✅ Redis连接测试结果: {}", "success".equals(result) ? "成功" : "失败");

            // 检查当前Redis中是否有产品数据
            String productData = redisTemplate.opsForValue().get(CacheConstants.IOT_ALL_PRODUCT_LIST);
            log.info("📊 Redis中现有产品数据: {}", productData != null ? "有数据" : "无数据");

        } catch (Exception e) {
            log.error("❌ Redis连接测试失败", e);
        }
    }


}
